﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Messaging;

using Octopus.Injection.Commons.Config;
using InjectionCof = Octopus.Injection.Commons.Config.Injection;

namespace Octopus.Injection.Commons.Interceptor.Injection
{
    internal class InjectionInspector : BaseInspector
    {
        internal InjectionInspector(IMessageSink nextSink, MarshalByRefObject value) : base(nextSink, value) { }
        
        internal override bool PreProcess(IMessage msg, MarshalByRefObject value)
        {
            base.PreProcess(msg, value);
            
            foreach (var propertyInfo in value.GetType().GetProperties())
            {
                object value2Inject = null;
                
                if(!string.IsNullOrEmpty(propertyInfo.PropertyType.FullName)
                    && ConfigRepository.ContractsTypes.ContainsKey(propertyInfo.PropertyType.FullName))
                {
                    var pair = ConfigRepository.ContractsTypes[propertyInfo.PropertyType.FullName];

                    //Si es 0 el contrato permite singleton
                    if(pair.Key == ConfigRepository.AllowSingleton)
                    {
                        value2Inject = pair.Value;    
                    }
                    else
                    {
                        value2Inject = Activator.CreateInstance(pair.Value.GetType());
                    }
                }

                if (value2Inject != null)
                {
                    propertyInfo.SetValue(value, value2Inject, null);
                }
            }

            var octoSec = ConfigurationManager.GetSection("spock/octopus") as OctopusSection;                 

            if (octoSec != null && octoSec.InjectionMap.Injections != null)
            {
                var injConf =
                    octoSec.InjectionMap.Injections.OfType<InjectionCof>().SingleOrDefault(
                        n => n.Class.Equals(value.GetType().AssemblyQualifiedName));

                if(injConf != null)
                {
                    foreach (Property property in injConf.Properties)
                    {
                        object value2Inject = null;
                        
                        var asm = string.Empty;
                        var typeName = property.Class;

                        if (typeName.Contains(","))
                        {
                            asm = typeName.Substring(typeName.IndexOf(',') + 1).Trim();
                            typeName = typeName.Split(',')[0].Trim();
                        }

                        if (!string.IsNullOrEmpty(property.Composition))
                        {
                            var aTypes = property.Composition.Split(';').ToList();
                            aTypes.ForEach(n => typeName += "[" + n + "]");
                        }

                        var type = new OctopusTypeBinder(true).BindToType(asm, typeName);
                        
                        if (type != null)
                        {
                            if (!string.IsNullOrEmpty(type.AssemblyQualifiedName) 
                                && property.Singleton)
                            {
                                if(ConfigRepository.InjectionSingletonMap.ContainsKey(type.AssemblyQualifiedName))
                                {
                                    value2Inject = ConfigRepository.InjectionSingletonMap[type.AssemblyQualifiedName];
                                }
                                else
                                {
                                    value2Inject = Activator.CreateInstance(type);    
                                    ConfigRepository.InjectionSingletonMap.Add(type.AssemblyQualifiedName, value2Inject);
                                }
                            }
                            else
                            {
                                value2Inject = Activator.CreateInstance(type);    
                            }
                        }

                        if(value2Inject != null)
                        {
                            value.GetType().GetProperty(property.Name).SetValue(value, value2Inject, null);
                        }
                    }
                }
            }

            return false;
        }
    }
}
